#include "NodeVisitor.h"

using namespace std;

void IndexVisitor::visit(RootNode *n) {
	for (vector<SP<ParseNode> >::iterator child = n->children.begin(); child < n->children.end(); ++child) {
		CompilationUnitNode *cun = (CompilationUnitNode*)(*child)();
		ClassDeclarationNode *cdn = (ClassDeclarationNode*)(*cun)["InterfaceDeclaration"];

		//If this compilation was an interface
		if (cdn != 0x0) {
			cdn->index = class_index++;

			//Add each of its interface methods to the global set of abstract methods
			set<TypedNode*, compare_decls> abstract_methods = cdn->elements_in_scope[DeclarationNode::Method];
			for (set<TypedNode*, compare_decls>::iterator method = abstract_methods.begin(); method != abstract_methods.end(); ++method) {
				//If the method isn't actually abstract, continue (happens because I was lazy; ask Mike about it, if there are issues)
				if (!((*method)->modifier & ModifiedNode::Abstract)) continue;
				//If this method signature doesn't have an index...
				if (interface_method_indexes.find(*method) == interface_method_indexes.end()) {
					//...give it a new, unique one
					interface_method_indexes[*method] = interface_method_indexes.size();
				}
			}
		}
	}
}
void IndexVisitor::visit(ClassDeclarationNode *n) {
	//subclasses of this class could have already asked this class to resolve its indexes, because they need it to be done first
	//which means we don't need to index this class again
	if (n->index < INT_MAX) return;

	map<TypedNode*, TypedNode*, compare_decls> my_method_bucket, my_constructor_bucket, my_static_field_bucket, parent_method_bucket;

	//this class' buckets
	set<TypedNode*, compare_decls> methods = n->elements_in_scope[DeclarationNode::Method];
	for (set<TypedNode*, compare_decls>::iterator method = methods.begin(); method != methods.end(); ++method) {
		if ((*(ParseNode*)*method).Type() == "ConstructorDeclaration")
			my_constructor_bucket[*method] = *method;
		else {
			my_method_bucket[*method] = *method;
			if ((*method)->identifier == "test" && (*method)->type_meta.category == TypeMeta::Int && (*method)->modifier & ModifiedNode::Static)
				start_method = (MethodDeclarationNode*)*method;
		}
	}

	//This is the counter for method position inside this class. Starts after the interface block
	unsigned int method_index = interface_method_indexes.size();

	//If we have a super class... 
	if (n->super) {
		//...and that super class has not been indexed yet...
		if (n->super->index == INT_MAX)
			//...visit our super first
			visit(n->super);

		//By this point, this class has not been indexed, but its super exists and has recursively been indexed

		// initializing my size to parent's
		class_sizes[n] = class_sizes[n->super];
		//Build the relevant buckets

		//Parent bucket
		set<TypedNode*, compare_decls> methods = n->super->elements_in_scope[DeclarationNode::Method];
		for (set<TypedNode*, compare_decls>::iterator method = methods.begin(); method != methods.end(); ++method) {
			if ((*(ParseNode*)*method).Type() != "ConstructorDeclaration")
				parent_method_bucket[*method] = *method;
		}

		//Index methods
		{
			method_index += parent_method_bucket.size();
			for (map<TypedNode*, TypedNode*, compare_decls>::iterator method = my_method_bucket.begin(); method != my_method_bucket.end(); ++method) {
				bool set_our_label = true;

				TypedNode *m = method->first;
				//The method is in the parent's set of methods. So we're overriding...
				if (parent_method_bucket.find(m) != parent_method_bucket.end()) {
					//...and we should use the parent's index
					m->index = parent_method_bucket[m]->index;

					//This is an inherited class...
					if (m->containing_class == parent_method_bucket[m]->containing_class) {
						//...we need to inherit the same label
						m->label = parent_method_bucket[m]->label;
						set_our_label = false;
					}

					//We found this method in the set of interface methods. This implies the method declaration was implementing...
				} else if (interface_method_indexes.find(m) != interface_method_indexes.end()) {
						//...this interface. So use its index
						m->index = interface_method_indexes[m];
						
						//This is a method, and not a constructor, index it now...
				} else {
					//...and we have to generate a new index
					m->index = method_index++;
				}

				//Set the label here, if needed
				if (set_our_label) {
					string base = m->containing_class->package_name + m->containing_class->identifier + "." + m->identifier;
					//If this method is native, generate a specific label
					if (m->modifier & ModifiedNode::Native) {
						base = "NATIVE" + base;
						m->label = base.substr(0, base.find_first_of('$'));
					} else m->label = base;

					global_labels.insert(m->label);
				}
			}
		}
		//This class is java.lang.Object
	} else {
		// initializing to size 1 for Object
		class_sizes[n] = 2;
		//Run through its methods, giving them unique indexes
		for (map<TypedNode*, TypedNode*, compare_decls>::iterator method = my_method_bucket.begin(); method != my_method_bucket.end(); ++method) {
			TypedNode *m = method->first;
			m->index = method_index++;
			m->label = n->package_name + n->type_name + "." + m->identifier;
			global_labels.insert(m->label);
		}
	}

	//index this class
	n->index = class_index++;
	n->label = n->package_name + n->identifier;
	global_labels.insert(n->label);

	for (map<TypedNode*, TypedNode*, compare_decls>::iterator constructor = my_constructor_bucket.begin(); constructor != my_constructor_bucket.end(); ++constructor) {
		TypedNode *c = constructor->first;
		c->index = method_index++;
		c->label = c->containing_class->package_name + c->containing_class->type_name + "." + c->identifier;
		global_labels.insert(c->label);
	}

	//Build field labels and add them to the global label exposing
	set<TypedNode*, compare_decls> fields = n->elements_in_scope[DeclarationNode::Expression];
	for (set<TypedNode*, compare_decls>::iterator field = fields.begin(); field != fields.end(); ++field) {
		if (!((*field)->modifier & ModifiedNode::Static)) {
			(*field)->index = class_sizes[n]++;
			(*field)->label = "eax";
			continue;
		}

		TypedNode *f = *field;
		my_static_field_bucket[f];

		f->label = f->containing_class->package_name + f->containing_class->type_name + "." + f->identifier + "?";
		f->index = 0;
		global_labels.insert(f->label);
	}
}